import java.io.*;
import java.util.Arrays;

/**
 * IO Streams Examples
 * This class demonstrates various I/O operations in Java including:
 * - File operations
 * - Byte streams
 * - Character streams
 * - Object serialization
 */
public class IOStreams {
    
    public static void main(String[] args) {
        IOStreams example = new IOStreams();
        
        // Demonstrating File class operations
        example.demonstrateFileClass();
        
        // Demonstrating byte streams
        example.demonstrateByteStreams();
        
        // Demonstrating character streams
        example.demonstrateCharacterStreams();
        
        // Demonstrating object serialization
        example.demonstrateSerialization();
    }
    
    /**
     * Method to demonstrate File class operations
     */
    public void demonstrateFileClass() {
        System.out.println("=== File Class Operations ===");
        
        // Creating File objects
        File file = new File("example.txt");
        File directory = new File("example_directory");
        
        // File creation
        try {
            if (file.createNewFile()) {
                System.out.println("File created: " + file.getName());
            } else {
                System.out.println("File already exists.");
            }
        } catch (IOException e) {
            System.out.println("An error occurred while creating the file.");
            e.printStackTrace();
        }
        
        // Directory creation
        if (directory.mkdir()) {
            System.out.println("Directory created: " + directory.getName());
        } else {
            System.out.println("Directory already exists or creation failed.");
        }
        
        // File properties
        System.out.println("File name: " + file.getName());
        System.out.println("Absolute path: " + file.getAbsolutePath());
        System.out.println("File exists: " + file.exists());
        System.out.println("File readable: " + file.canRead());
        System.out.println("File writable: " + file.canWrite());
        System.out.println("File size (bytes): " + file.length());
        
        // Directory listing
        File currentDir = new File(".");
        String[] files = currentDir.list();
        if (files != null) {
            System.out.println("Files in current directory: " + Arrays.toString(files));
        }
        
        // Cleanup
        if (file.exists()) {
            file.delete();
            System.out.println("File deleted.");
        }
        
        if (directory.exists()) {
            directory.delete();
            System.out.println("Directory deleted.");
        }
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate byte streams
     */
    public void demonstrateByteStreams() {
        System.out.println("=== Byte Streams ===");
        
        String data = "This is a sample text for byte stream demonstration.\nHello World!";
        byte[] dataBytes = data.getBytes();
        
        // FileOutputStream example
        try (FileOutputStream fos = new FileOutputStream("byte_output.txt")) {
            // Writing data to file
            fos.write(dataBytes);
            System.out.println("Data written to byte_output.txt using FileOutputStream");
        } catch (IOException e) {
            System.out.println("An error occurred while writing to file.");
            e.printStackTrace();
        }
        
        // FileInputStream example
        try (FileInputStream fis = new FileInputStream("byte_output.txt")) {
            // Reading data from file
            int content;
            System.out.print("Data read from byte_output.txt using FileInputStream: ");
            while ((content = fis.read()) != -1) {
                System.out.print((char) content);
            }
            System.out.println();
        } catch (IOException e) {
            System.out.println("An error occurred while reading from file.");
            e.printStackTrace();
        }
        
        // BufferedInputStream and BufferedOutputStream example
        try (FileOutputStream fos = new FileOutputStream("buffered_output.txt");
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // Writing data using buffered output stream
            bos.write(dataBytes);
            bos.flush(); // Ensuring data is written
            System.out.println("Data written to buffered_output.txt using BufferedOutputStream");
        } catch (IOException e) {
            System.out.println("An error occurred while writing to file with buffering.");
            e.printStackTrace();
        }
        
        try (FileInputStream fis = new FileInputStream("buffered_output.txt");
             BufferedInputStream bis = new BufferedInputStream(fis)) {
            
            // Reading data using buffered input stream
            byte[] buffer = new byte[1024];
            int bytesRead = bis.read(buffer);
            System.out.println("Data read from buffered_output.txt using BufferedInputStream: " + 
                              new String(buffer, 0, bytesRead));
        } catch (IOException e) {
            System.out.println("An error occurred while reading from file with buffering.");
            e.printStackTrace();
        }
        
        // Cleanup
        File file1 = new File("byte_output.txt");
        File file2 = new File("buffered_output.txt");
        if (file1.exists()) file1.delete();
        if (file2.exists()) file2.delete();
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate character streams
     */
    public void demonstrateCharacterStreams() {
        System.out.println("=== Character Streams ===");
        
        String data = "This is a sample text for character stream demonstration.\nHello World!";
        
        // FileWriter example
        try (FileWriter fw = new FileWriter("char_output.txt")) {
            // Writing data to file
            fw.write(data);
            System.out.println("Data written to char_output.txt using FileWriter");
        } catch (IOException e) {
            System.out.println("An error occurred while writing to file.");
            e.printStackTrace();
        }
        
        // FileReader example
        try (FileReader fr = new FileReader("char_output.txt")) {
            // Reading data from file
            int content;
            System.out.print("Data read from char_output.txt using FileReader: ");
            while ((content = fr.read()) != -1) {
                System.out.print((char) content);
            }
            System.out.println();
        } catch (IOException e) {
            System.out.println("An error occurred while reading from file.");
            e.printStackTrace();
        }
        
        // BufferedWriter and BufferedReader example
        try (FileWriter fw = new FileWriter("buffered_char_output.txt");
             BufferedWriter bw = new BufferedWriter(fw)) {
            
            // Writing data using buffered writer
            bw.write(data);
            bw.newLine(); // Writing a new line
            bw.write("This is a second line.");
            System.out.println("Data written to buffered_char_output.txt using BufferedWriter");
        } catch (IOException e) {
            System.out.println("An error occurred while writing to file with buffering.");
            e.printStackTrace();
        }
        
        try (FileReader fr = new FileReader("buffered_char_output.txt");
             BufferedReader br = new BufferedReader(fr)) {
            
            // Reading data using buffered reader
            String line;
            System.out.println("Data read from buffered_char_output.txt using BufferedReader:");
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("An error occurred while reading from file with buffering.");
            e.printStackTrace();
        }
        
        // Cleanup
        File file1 = new File("char_output.txt");
        File file2 = new File("buffered_char_output.txt");
        if (file1.exists()) file1.delete();
        if (file2.exists()) file2.delete();
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate object serialization
     */
    public void demonstrateSerialization() {
        System.out.println("=== Object Serialization ===");
        
        // Creating an object to serialize
        Person1 person = new Person1("John Doe", 30, "john@example.com");
        
        // Serializing the object
        try (FileOutputStream fos = new FileOutputStream("person.ser");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            
            oos.writeObject(person);
            System.out.println("Person object serialized to person.ser");
            System.out.println("Serialized person: " + person);
            
        } catch (IOException e) {
            System.out.println("An error occurred during serialization.");
            e.printStackTrace();
        }
        
        // Deserializing the object
        try (FileInputStream fis = new FileInputStream("person.ser");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            
            Person1 deserializedPerson = (Person1) ois.readObject();
            System.out.println("Person object deserialized from person.ser");
            System.out.println("Deserialized person: " + deserializedPerson);
            
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("An error occurred during deserialization.");
            e.printStackTrace();
        }
        
        // Cleanup
        File file = new File("person.ser");
        if (file.exists()) file.delete();
        
        System.out.println();
    }
}

/**
 * A simple class to demonstrate object serialization
 */
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private String email;
    
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    
    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getEmail() { return email; }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", email='" + email + "'}";
    }
}